package de.unihannover.hci.portraitquiz;

import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.DrawableContainer;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.os.Vibrator;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import de.unihannover.hci.portraitquiz.adapter.ScoreArrayAdapter;
import de.unihannover.hci.portraitquiz.helper.ObstacleImageView;
import de.unihannover.hci.portraitquiz.model.Answer;
import de.unihannover.hci.portraitquiz.model.Score;

public class GamePlayActivity extends Activity {

	private CountDownTimer countDown;
	private Date questionStarted;

	private PortraitQuiz app;
	private List<Answer> answers;

	private Button answer1;
	private Button answer2;
	private Button answer3;
	private Button answer4;

	/**
	 * reffers to the current question in the answers List;
	 */
	private int questionCount;
	private int wrongAnswers;
	private int points;
	private Vibrator vibrator;

	private ObstacleImageView faceImage;

	private boolean gameRunning;
	private Context context;
	private MediaPlayer mp;
	private Animation anim_shake;
	private Animation anim_correct;
	private ImageView titleLogo;
	private Animation anim_timeover;
	private Animation anim_drivein;
	private TextView timeOver;
	private Animation anim_stars;
	private Drawable portrait_drawable;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
				WindowManager.LayoutParams.FLAG_FULLSCREEN);
		setContentView(R.layout.activity_game_play);
		faceImage = (ObstacleImageView) findViewById(R.id.imageView);
		faceImage.buildDrawingCache();
		app = (PortraitQuiz) getApplication();
		context = getBaseContext();
		portrait_drawable = getResources().getDrawable(R.drawable.actors_tomhanks);

		// Set fonts
		TextView title1 = (TextView) findViewById(R.id.title);
		TextView title2 = (TextView) findViewById(R.id.title2);
		TextView cat = (TextView) findViewById(R.id.category);
		timeOver = (TextView) findViewById(R.id.timeisover);
		Button replay_btn = (Button) findViewById(R.id.replay_btn);
		Button backToMenu_btn = (Button) findViewById(R.id.backToMenu);
		TextView countDownText = (TextView) findViewById(R.id.countdown);
		Typeface type = app.getAppFontType();
		title1.setTypeface(type);
		title2.setTypeface(type);
		cat.setTypeface(type);
		timeOver.setTypeface(type);
		replay_btn.setTypeface(type);
		backToMenu_btn.setTypeface(type);
		countDownText.setTypeface(type);

		TextView category = (TextView) findViewById(R.id.category);
		category.setText(app.getCurrentCategory().getName());
		answers = app.getCurrentCategory().getAnswers();
		Collections.shuffle(answers);

		vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);

		OnClickListener clickListener = new OnClickListener() {
			public void onClick(View v) {

				vibrator.vibrate(new long[] { 0, 50, 180, 50 }, -1);
				validateAnswer((Button) v);
			}
		};

		answer1 = (Button) findViewById(R.id.answer1);
		answer1.setOnClickListener(clickListener);

		answer2 = (Button) findViewById(R.id.answer2);
		answer2.setOnClickListener(clickListener);

		answer3 = (Button) findViewById(R.id.answer3);
		answer3.setOnClickListener(clickListener);

		answer4 = (Button) findViewById(R.id.answer4);
		answer4.setOnClickListener(clickListener);

		// Buttons in der Highscore-Übersicht mit Leben füllen
		Button backToMenu = (Button) findViewById(R.id.backToMenu);
		backToMenu.setOnClickListener(new OnClickListener() {
			public void onClick(View v) {
				finish();
			}
		});

		Button playAgain = (Button) findViewById(R.id.replay_btn);
		playAgain.setOnClickListener(new OnClickListener() {
			public void onClick(View v) {
				Intent intent = getIntent();
				finish();
				startActivity(intent);
			}
		});

		gameRunning = true;

		// Musik-Vorbereitungen
		mp = MediaPlayer.create(GamePlayActivity.this, R.raw.jeopardy);
		mp.setOnCompletionListener(new OnCompletionListener() {
			public void onCompletion(MediaPlayer mp) {
				mp.release();
			}
		});
		mp.setVolume(1, 1);
		mp.setLooping(true);

		// Image-Animation
		anim_shake = AnimationUtils.loadAnimation(this, R.anim.faceimg_shake_anim);
		anim_correct = AnimationUtils.loadAnimation(this, R.anim.faceimg_correct_anim);
		titleLogo = (ImageView) findViewById(R.id.titleLogo);
		anim_timeover = AnimationUtils.loadAnimation(this, R.anim.timeover_anim);
		anim_drivein = AnimationUtils.loadAnimation(this, R.anim.logodrivein_anim);
		anim_stars = AnimationUtils.loadAnimation(this, R.anim.starsdrivein_anim);

		startGetReadyCountDown();
	}

	private void prepareNextRound() {
		questionStarted = new Date();
		// questionCount++;
		CountDownTimer obstacleTimer = new CountDownTimer(30000, 700) {

			@Override
			public void onTick(long ms) {
				/*
				 * faceImage.decreaseObstacleAlpha(35);
				 * faceImage.buildDrawingCache(false);
				 */
			}

			@Override
			public void onFinish() {
				// TODO Auto-generated method stub

			}
		};

		if (questionCount <= answers.size() - 1 && gameRunning) {
			setImage(answers.get(questionCount).getImage());
			updateAnswers();
			answer1.setClickable(true);
			answer2.setClickable(true);
			answer3.setClickable(true);
			answer4.setClickable(true);
		} else {
			answer1.setClickable(false);
			answer2.setClickable(false);
			answer3.setClickable(false);
			answer4.setClickable(false);
			Toast.makeText(getBaseContext(), "Really fast! No persons left.", Toast.LENGTH_SHORT).show();
		}

		obstacleTimer.start();
	}

	private void validateAnswer(Button button) {
		answer1.setClickable(false);
		answer2.setClickable(false);
		answer3.setClickable(false);
		answer4.setClickable(false);

		// Dauer der Verzögerung zum Anzeigen der richtigen/falschen Antwort
		int millisInFuture;

		// Korrekte Antwort
		if (button.getText().equals(answers.get(questionCount).getName())) {
			// Faceimage animieren
			faceImage.startAnimation(anim_correct);
			// Kurze einfache Vibration für richtige Antwort
			vibrator.vibrate(new long[] { 0, 50 }, -1);
			calculateCorrectAnswer();
			buttonStyleCorrectAnswer(button);
			// Kurze Verzögerung bei richtiger Antwort
			millisInFuture = 300;
		} else { // Falsche Antwort
			// Faceimage animieren
			faceImage.startAnimation(anim_shake);
			// Kurze Doppel-Vibration für falsche Antwort
			vibrator.vibrate(new long[] { 0, 50, 100, 50 }, -1);
			calculateWrongAnswer();
			buttonStyleWrongAnswer(button);
			// Lange Verzögerung bei falscher Antwort
			millisInFuture = 1200;

			Button[] buttons = { answer1, answer2, answer3, answer4 };
			for (Button btn : buttons) {
				if (btn.getText().equals(answers.get(questionCount).getName())) {
					btn.setText(btn.getText()); // Workaround damit die
												// Hintergrundfarbe sichtbar
												// wird...
					buttonStyleCorrectAnswer(btn);
				}
			}
		}

		questionCount++;

		// Kurze Verzögerung, damit das korrekte Ergebnis noch wahrgenommen
		// werden kann
		CountDownTimer gameTimer = new CountDownTimer(millisInFuture, 100) {
			@Override
			public void onTick(long millisUntilFinished) {
			}

			@Override
			public void onFinish() {
				if (gameRunning)
					prepareNextRound();

			}
		};
		gameTimer.start();
	}

	private static void buttonStyleCorrectAnswer(Button button) {
		button.getBackground().setColorFilter(0xFF00FF00, PorterDuff.Mode.MULTIPLY);
	}

	private static void buttonStyleWrongAnswer(Button button) {
		button.getBackground().setColorFilter(0xFFFF0000, PorterDuff.Mode.MULTIPLY);
	}

	private void calculateWrongAnswer() {
		wrongAnswers++;
	}

	private void calculateCorrectAnswer() {
		long diff = (new Date()).getTime() - questionStarted.getTime();
		int pow = (int) Math.pow(diff / 100, 0.5);
		points += 10000 / pow;
	}

	private void calculateTotalHighscore() {
		if (questionCount == 0) {
			points = 0;
		} else {
			points = points * (questionCount - wrongAnswers) / questionCount;
		}
	}

	/**
	 * Creates a shuffled list of answers and set the labels of the answer
	 * buttons
	 */
	private void updateAnswers() {
		List<String> currentAnswers = new LinkedList<String>();

		// Gestreute Antwortmöglichkeiten
		int a = (questionCount + 3) % answers.size();
		int b = (questionCount + 6) % answers.size();
		int c = (questionCount + 9) % answers.size();

		currentAnswers.add(answers.get(questionCount).getName());
		currentAnswers.add(answers.get(a).getName());
		currentAnswers.add(answers.get(b).getName());
		currentAnswers.add(answers.get(c).getName());

		// Gesammelte Antworten mischen, damit korrekte Antwort nicht immer an
		// erster Stelle steht
		Collections.shuffle(currentAnswers);

		// Text setzen und Buttons wieder normal färben
		answer1.setText(currentAnswers.get(0));
		answer1.getBackground().clearColorFilter();
		answer2.setText(currentAnswers.get(1));
		answer2.getBackground().clearColorFilter();
		answer3.setText(currentAnswers.get(2));
		answer3.getBackground().clearColorFilter();
		answer4.setText(currentAnswers.get(3));
		answer4.getBackground().clearColorFilter();
	}

	private void setImage(String imagePath) {
		InputStream inputStream = null;

		try {
			inputStream = this.getAssets().open("answers/" + imagePath);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		// create drawable from stream

		// Sehr wichtig, um VM-outOfMemoryException zu verhindern. Insbesondere
		// nicht jedesmal ein neues Drawable-Objekt erzeugen.
		portrait_drawable.setCallback(null);
		portrait_drawable = Drawable.createFromStream(inputStream, null);
		if (app.getObstacle() != null) {
			faceImage.setObstacle(app.getObstacle());
		}
		faceImage.setImage(portrait_drawable);
		System.gc(); // ebenfalls gegen VM-outOfMemoryException
	}

	/**
	 * Steuert den get ready Screen beim Starten des Spiels
	 */
	private void startGetReadyCountDown() {
		final LinearLayout getReadyOverlay = (LinearLayout) findViewById(R.id.getReadyOverlay);
		getReadyOverlay.setVisibility(View.VISIBLE);
		final TextView t = (TextView) findViewById(R.id.countdown);

		countDown = new CountDownTimer((int) (4 * 1000), 100) {
			@Override
			public void onTick(long millisUntilFinished) {
				int dif = (int) (millisUntilFinished / 1000);
				if (dif > 0) {
					t.setText("" + millisUntilFinished / 1000);
				} else {
					t.setText("Get ready!");
				}
			}

			@Override
			public void onFinish() {
				getReadyOverlay.setVisibility(View.GONE);
				startGame();
			}
		};
		countDown.start();
	}

	private void showHighscore() {
		// Punktestand setzen
		final TextView points_textView = (TextView) findViewById(de.unihannover.hci.portraitquiz.R.id.points);
		points_textView.setText("" + this.points);

		// Played Question Anzahl setzen
		final TextView playedQuest_textView = (TextView) findViewById(R.id.playedQuestions);
		if (questionCount < 0)
			questionCount = 0;
		playedQuest_textView.setText("" + questionCount);

		// Correct Answers Anzahl und Prozentwert setzen
		int correctAnswers = questionCount - wrongAnswers;
		double correctPerc = Math.round(((double) correctAnswers / questionCount * 100) * 100.) / 100.;
		final TextView correctAnsw_textView = (TextView) findViewById(R.id.correctAnswers);
		correctAnsw_textView.setText(correctAnswers + "  (" + correctPerc + " %)");

		final ImageView highscoreStars = (ImageView) findViewById(de.unihannover.hci.portraitquiz.R.id.highscore_stars);
		int ranking = app.getStarsDrawableForScore(points);
		highscoreStars.setImageResource(ranking);

		// ListView befüllen
		List<Score> scores = app.db.getAllHighscoreItems();
		ListView higscoreList = (ListView) findViewById(R.id.highscoreList);
		ScoreArrayAdapter adapter = new ScoreArrayAdapter(this, scores);
		higscoreList.setAdapter(adapter);

		// Animation "Logo ausfahren"
		titleLogo.startAnimation(anim_timeover);

		CountDownTimer ct2 = new CountDownTimer(2500, 1000) {

			@Override
			public void onFinish() {
				// Highscore Overlay einblenden
				final LinearLayout highscoreOverlay = (LinearLayout) findViewById(R.id.highscoreOverlay);
				highscoreOverlay.setVisibility(View.VISIBLE);
				// Logo und Stars reinfahren/einblenden
				titleLogo.startAnimation(anim_drivein);
				highscoreStars.startAnimation(anim_stars);

			}

			@Override
			public void onTick(long millisUntilFinished) {
				// TODO Auto-generated method stub

			}
		};
		ct2.start();
	}

	private void startGame() {
		startCountDownTimer();
		if (app.playsSounds() == true)
			mp.start();
		questionCount = 0;
		points = 0;
		wrongAnswers = 0;
		prepareNextRound();
	}

	/**
	 * Steuerung des Count Down Timers während des spiels
	 */
	private void startCountDownTimer() {
		final LinearLayout highscoreOverlay = (LinearLayout) findViewById(R.id.highscoreOverlay);
		highscoreOverlay.setVisibility(View.GONE);

		final ProgressBar gameProgress = (ProgressBar) findViewById(R.id.gameProgress);
		countDown = new CountDownTimer(30 * 1000, 500) {

			@Override
			public void onTick(long millisUntilFinished) {
				System.out.println("millisuntilfinished: " + millisUntilFinished);
				if (millisUntilFinished <= 5000 && millisUntilFinished > 4500) {
					// ToneGenerator tone = new
					// ToneGenerator(AudioManager.STREAM_DTMF, 50);
					// tone.startTone(ToneGenerator.TONE_DTMF_9, 500);
					Rect bounds = gameProgress.getProgressDrawable().getBounds();
					gameProgress.setProgressDrawable(getResources().getDrawable(R.drawable.gameprogressbar_finish));
					gameProgress.getProgressDrawable().setBounds(bounds);
				}

				gameProgress.setProgress((int) (millisUntilFinished / 1000));

			}

			@Override
			public void onFinish() {
				gameRunning = false;
				answer1.setClickable(false);
				answer2.setClickable(false);
				answer3.setClickable(false);
				answer4.setClickable(false);
				gameProgress.setVisibility(View.INVISIBLE);
				timeOver.setVisibility(View.VISIBLE);

				mp.stop();
				mp.release();
				calculateTotalHighscore();
				Score score = new Score();
				score.setCategory(app.getCurrentCategory().getId());
				score.setPoints(points);
				score.setDatetime(new Date());
				score.setAnswers_correct(questionCount - wrongAnswers);
				score.setAnswers_played(questionCount);
				app.db.addHighScoreEntry(score);

				showHighscore();
			}
		};
		countDown.start();
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.activity_game_play, menu);
		return true;
	}

	@Override
	public boolean onKeyDown(int keycode, KeyEvent event) {
		if (keycode == KeyEvent.KEYCODE_MENU && gameRunning == true) {
			showPausedDialog();
		}
		return super.onKeyDown(keycode, event);
	}

	@SuppressLint("NewApi")
	private void showPausedDialog() {
		if (gameRunning == true) {
			gameRunning = false;
			mp.stop();
			mp.release();
			countDown.cancel();
			final AlertDialog.Builder builder = (android.os.Build.VERSION.SDK_INT < 11) ? new AlertDialog.Builder(this)
					: new AlertDialog.Builder(this, AlertDialog.THEME_HOLO_DARK);
			builder.setTitle("Game Paused");
			builder.setMessage("What next?");
			builder.setIcon(R.drawable.paused);
			builder.setCancelable(false);
			builder.setPositiveButton("Restart", new DialogInterface.OnClickListener() {

				public void onClick(DialogInterface dialog, int which) {
					Intent intent = getIntent();
					finish();
					startActivity(intent);
				}
			});
			builder.setNegativeButton("Back To Menu", new DialogInterface.OnClickListener() {

				public void onClick(DialogInterface dialog, int which) {
					finish();
				}
			});
			AlertDialog dialog = builder.create();
			dialog.show();
		}
	}

	@Override
	protected void onPause() {
		showPausedDialog();
		super.onPause();
	}
}
